using System;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using PhotoUtil;
using SortMyPictures.DirectoryRemover;

namespace SortMyPictures
{
    /// <summary>
    /// Main Form
    /// </summary>
    public partial class Form1 : Form
    {
        /// <summary>
        /// </summary>
        internal const string PredefinedTimeFormat = "yyyy:MM:dd hh:mm:ss";
        /// <summary>
        /// </summary>
        private long _allFileKB;
        /// <summary>
        /// </summary>
        private long _processedKB;
        /// <summary>
        /// </summary>
        private RemainingTime _remaining;
        /// <summary>
        /// </summary>
        private bool _StopProcessing = false;

        /// <summary>
        /// </summary>
        private readonly ICleaner cleaner;
        /// <summary>
        /// </summary>
        private readonly IPathValidator pathValidator;

        /// <summary>
        /// Initializes a new instance of the <see cref="Form1"/> class.
        /// </summary>
        public Form1()
        {
            InitializeComponent();
            cleaner = new Cleaner { Context = SynchronizationContext.Current };
            cleaner.CleaningDone += CleaningDone;
        }

        /// <summary>
        /// Handles the Load event of the Form1 control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void Form1_Load(object sender, EventArgs e)
        {
            InitValues();
        }

        /// <summary>
        /// Inits the values.
        /// </summary>
        private void InitValues()
        {
            textboxFolder.Text = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
            SortMyPictures.Properties.Settings sett = new SortMyPictures.Properties.Settings();
            if (sett.SortDirStartupPath == true)
            {
                textboxScanLocation.Text = Application.StartupPath;
            }
            else
            {
                textboxScanLocation.Text = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
            }
        }

        /// <summary>
        /// Handles the Click event of the buttonSort control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void buttonSort_Click(object sender, EventArgs e)
        {
            buttonStop.Enabled = true;
            listView1.Items.Clear();

            //int progressCount = 0;
            int ttlMovable = 0;
            string[] fNames = Directory.GetFiles(textboxScanLocation.Text, "*.*", SearchOption.AllDirectories);
            TotalKBForFiles(fNames);
            _remaining = new RemainingTime(_allFileKB);
            labelTotal.Text = "Total Items: " + fNames.Length.ToString();
            int ttlIgnore = 0;

            foreach (string s in fNames)
            {
                DateTime startDateTime = DateTime.Now;
                if (_StopProcessing)
                {
                    MessageBox.Show("Processing cancelled by user.");
                    _StopProcessing = false;
                    buttonStop.Enabled = false;
                    return;
                }
                //progressCount++;
                if (IsImageFile(s))
                {
                    FileAttributes fab = File.GetAttributes(s);
                    if ((fab & FileAttributes.ReadOnly) != FileAttributes.ReadOnly)
                    {
                        //TODO thumbnailPanel1.AddPicture(s, true);
                        ListViewItem lvi = new ListViewItem();
                        lvi.Text = Path.GetFileName(s);
                        lvi.SubItems.Add(s);
                        try
                        {
                            System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(s);
                            pictureBox1.Image = bmp;
                            Application.DoEvents();
                            string dtStamp = "Not available!";
                            try
                            {
                                dtStamp = ExifHelper.GetImageTakenTimeString(bmp, PredefinedTimeFormat);
                                if (dtStamp != "Not available!")
                                {
                                    lvi.SubItems.Add(dtStamp);
                                }
                                else
                                {
                                    dtStamp = File.GetLastWriteTime(s).ToString();
                                    lvi.SubItems.Add(dtStamp);
                                }
                            }
                            catch (Exception)
                            {
                                lvi.SubItems.Add("Cannot Open");
                                lvi.ForeColor = Color.Red;
                                pictureBox1.Image = null;
                                bmp.Dispose();
                                bmp = null;
                                GC.Collect();
                            }

                            lvi.SubItems.Add(ExifHelper.GetCameraMake(bmp)); // Make
                            lvi.SubItems.Add(ExifHelper.GetCameraModel(bmp)); //Model
                            if (dtStamp != "Not available!" && dtStamp.Trim().Length != 0)
                            {
                                string destFolderName = BuildFolderName(dtStamp);
                                if (destFolderName.Trim().Length > 0)
                                {
                                    if (s.Length > destFolderName.Length)
                                    {
                                        if (s.Substring(0, destFolderName.Length).ToUpper() != destFolderName.ToUpper())
                                        {
                                            lvi.SubItems.Add(Path.Combine(destFolderName, Path.GetFileName(s)));
                                                // Destination Folder
                                            ttlMovable++;
                                            labelMovable.Text = "Movable: " + ttlMovable.ToString();
                                        }
                                        else
                                        {
                                            lvi.SubItems.Add(""); // No need for destination folder.
                                        }
                                    }
                                    else
                                    {
                                        lvi.SubItems.Add(Path.Combine(destFolderName, Path.GetFileName(s)));
                                            // Destination Folder
                                        ttlMovable++;
                                        labelMovable.Text = "Movable: " + ttlMovable.ToString();
                                    }
                                }
                            }
                            else
                            {
                                lvi.SubItems.Add("");
                            }
                            pictureBox1.Image = null;
                            bmp.Dispose();
                            bmp = null;
                            ////////exi = null;
                            GC.Collect();
                        }
                        catch (Exception)
                        {
                            lvi.SubItems.Add("");
                            lvi.SubItems.Add("");
                            lvi.SubItems.Add("");
                            lvi.SubItems.Add("");
                            GC.Collect();
                        }
                        string fHash = FileHash.ComputeMD5Hash(s);
                        lvi.SubItems.Add(fHash);
                        if (IsDuplicate(fHash))
                        {
                            lvi.ForeColor = Color.Red;
                        }
                        listView1.Items.Add(lvi);
                        labelCount.Text = "Processed " + listView1.Items.Count.ToString();
                        //listView1.EnsureVisible(listView1.Items.Count - 1);
                    }
                    else
                    {
                        ttlIgnore++;
                        labelIgnore.Text = "Ignored " + ttlIgnore.ToString();
                        listviewLog.Items.Add(new ListViewItem("Ignored ReadOnly File - " + s));
                    }
                }
                else
                {
                    ttlIgnore++;
                    labelIgnore.Text = "Ignored " + ttlIgnore.ToString();
                    listviewLog.Items.Add(new ListViewItem("Ignored File - " + s));
                }
                TimeSpan ts = DateTime.Now.Subtract(startDateTime);
                //BDP CalculateRemaining(listView1.Items.Count + ttlIgnore,fNames.Length,ts.TotalSeconds);
                FileInfo fi = new FileInfo(s);
                CalculateRemainingTimeViaKB(s, fi.Length, ts.Milliseconds);
            }
            button1.Enabled = true;
            buttonSort.Enabled = false;
            MessageBox.Show("Sort complete. Review results and click Move when ready.");

            if (DuplicateCount() > 0)
            {
                DialogResult drYESNo =
                    MessageBox.Show(
                        "You have " + DuplicateCount().ToString() + " duplicate file(s)." + Environment.NewLine +
                        "Would you like to delete these files?", "Delete Duplicates", MessageBoxButtons.YesNo,
                        MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);
                if (drYESNo == DialogResult.Yes)
                {
                    foreach (ListViewItem lvi in listView1.Items)
                    {
                        if (lvi.ForeColor == Color.Red)
                        {
                            File.Delete(lvi.SubItems[1].Text);
                        }
                    }
                }
            }
            buttonStop.Enabled = false;
        }

        /// <summary>
        /// Gets the file size KB.
        /// </summary>
        /// <param name="bytes">The bytes.</param>
        /// <returns></returns>
        public static long GetFileSizeKB(long bytes)
        {
            double num4;
            long num2 = bytes/0x400L;
            return num2;
        }

        /// <summary>
        /// Totals the KB for files.
        /// </summary>
        /// <param name="fNames">The f names.</param>
        private void TotalKBForFiles(string[] fNames)
        {
            long ttlKB = 0;
            foreach (string s in fNames)
            {
                if (IsImageFile(s))
                {
                    FileAttributes fab = File.GetAttributes(s);
                    if ((fab & FileAttributes.ReadOnly) != FileAttributes.ReadOnly)
                    {
                        FileInfo fi = new FileInfo(s);
                        ttlKB += GetFileSizeKB(fi.Length);
                    }
                }
            }
            labelTotalSize.Text = "Total Size: " + ttlKB.ToString("N") + " KB";
            _allFileKB = ttlKB;
        }

        /// <summary>
        /// Duplicates the count.
        /// </summary>
        /// <returns></returns>
        private int DuplicateCount()
        {
            int ttlDuplicates = 0;
            foreach (ListViewItem lvi in listView1.Items)
            {
                if (lvi.ForeColor == Color.Red)
                {
                    ttlDuplicates++;
                }
            }
            return ttlDuplicates;
        }


        /// <summary>
        /// Determines whether the specified hash is duplicate.
        /// </summary>
        /// <param name="hash">The hash.</param>
        /// <returns>
        /// 	<c>true</c> if the specified hash is duplicate; otherwise, <c>false</c>.
        /// </returns>
        private bool IsDuplicate(string hash)
        {
            foreach (ListViewItem lvi in listView1.Items)
            {
                if (hash.ToUpper() == lvi.SubItems[6].Text.ToUpper())
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Calculates the remaining time via KB.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="fileByteSize">Size of the file byte.</param>
        /// <param name="milliSecondsForFile">The milli seconds for file.</param>
        private void CalculateRemainingTimeViaKB(string fileName, long fileByteSize, int milliSecondsForFile)
        {
            if (IsImageFile(fileName))
            {
                FileAttributes fab = File.GetAttributes(fileName);
                if ((fab & FileAttributes.ReadOnly) != FileAttributes.ReadOnly)
                {
                    long fileKB = GetFileSizeKB(fileByteSize);
                    _remaining.ProcessedFile(fileKB);
                    int totalMinutes = (int) _remaining.TimeRemaining.TotalMinutes;
                    if (totalMinutes > 0)
                    {
                        labelRemaining.Text = "Remaining: " + totalMinutes.ToString() +
                                              " minute(s)";
                    }
                    else
                    {
                        labelRemaining.Text = "Remaining: Less than 1 minute.";
                    }


                    labelSpeed.Text = _remaining.KBSec.ToString("N") + " KB/sec";
                    labelAvgSpeed.Text = _remaining.KBSecAvg.ToString("N") + " KB/sec";
                }
            }
        }

        /// <summary>
        /// Builds the name of the folder.
        /// </summary>
        /// <param name="dtStamp">The dt stamp.</param>
        /// <returns></returns>
        private string BuildFolderName(string dtStamp)
        {
            string[] dtParts1 = dtStamp.Split(new char[] {':'});
            if (dtParts1.Length >= 5)
            {
                try
                {
                    string[] dtParts = dtStamp.Split(new char[] {':'});
                    string newFolder = dtParts[0] + "-" + dtParts[1];

                    switch (Convert.ToInt32(dtParts[1]))
                    {
                        case 1:
                            newFolder += " (Jan)";
                            break;
                        case 2:
                            newFolder += " (Feb)";
                            break;
                        case 3:
                            newFolder += " (Mar)";
                            break;
                        case 4:
                            newFolder += " (Apr)";
                            break;
                        case 5:
                            newFolder += " (May)";
                            break;
                        case 6:
                            newFolder += " (Jun)";
                            break;
                        case 7:
                            newFolder += " (Jul)";
                            break;
                        case 8:
                            newFolder += " (Aug)";
                            break;
                        case 9:
                            newFolder += " (Sep)";
                            break;
                        case 10:
                            newFolder += " (Oct)";
                            break;
                        case 11:
                            newFolder += " (Nov)";
                            break;
                        case 12:
                            newFolder += " (Dec)";
                            break;
                    }
                    return Path.Combine(textboxFolder.Text, newFolder.Replace("\0", ""));
                }
                catch (Exception)
                {
                    return "";
                }
            }
            else if (dtParts1.Length == 3)
            {
                try
                {
                    string[] dtParts = dtStamp.Split(new char[] {'/'});
                    string newFolder = "";
                    if (dtParts[0].Length == 2)
                        newFolder = dtParts[2].Substring(0, 4) + "-" + dtParts[0];
                    else
                        newFolder = dtParts[2].Substring(0, 4) + "-0" + dtParts[0];

                    switch (Convert.ToInt32(dtParts[0]))
                    {
                        case 1:
                            newFolder += " (Jan)";
                            break;
                        case 2:
                            newFolder += " (Feb)";
                            break;
                        case 3:
                            newFolder += " (Mar)";
                            break;
                        case 4:
                            newFolder += " (Apr)";
                            break;
                        case 5:
                            newFolder += " (May)";
                            break;
                        case 6:
                            newFolder += " (Jun)";
                            break;
                        case 7:
                            newFolder += " (Jul)";
                            break;
                        case 8:
                            newFolder += " (Aug)";
                            break;
                        case 9:
                            newFolder += " (Sep)";
                            break;
                        case 10:
                            newFolder += " (Oct)";
                            break;
                        case 11:
                            newFolder += " (Nov)";
                            break;
                        case 12:
                            newFolder += " (Dec)";
                            break;
                    }
                    return Path.Combine(textboxFolder.Text, newFolder.Replace("\0", ""));
                }
                catch (Exception)
                {
                    return "";
                }
            }
            else
            {
                try
                {
                    string[] dtParts = dtStamp.Split(new char[] {'.'});
                    string newFolder = dtParts[2].Substring(0, 4) + "-" + dtParts[0];

                    switch (Convert.ToInt32(dtParts[0]))
                    {
                        case 1:
                            newFolder += " (Jan)";
                            break;
                        case 2:
                            newFolder += " (Feb)";
                            break;
                        case 3:
                            newFolder += " (Mar)";
                            break;
                        case 4:
                            newFolder += " (Apr)";
                            break;
                        case 5:
                            newFolder += " (May)";
                            break;
                        case 6:
                            newFolder += " (Jun)";
                            break;
                        case 7:
                            newFolder += " (Jul)";
                            break;
                        case 8:
                            newFolder += " (Aug)";
                            break;
                        case 9:
                            newFolder += " (Sep)";
                            break;
                        case 10:
                            newFolder += " (Oct)";
                            break;
                        case 11:
                            newFolder += " (Nov)";
                            break;
                        case 12:
                            newFolder += " (Dec)";
                            break;
                    }
                    return Path.Combine(textboxFolder.Text, newFolder.Replace("\0", ""));
                }
                catch (Exception)
                {
                    return "";
                }
            }
        }

        /// <summary>
        /// Determines whether [is image file] [the specified file name path].
        /// </summary>
        /// <param name="fileNamePath">The file name path.</param>
        /// <returns>
        /// 	<c>true</c> if [is image file] [the specified file name path]; otherwise, <c>false</c>.
        /// </returns>
        private bool IsImageFile(string fileNamePath)
        {
            string[] validExt = new string[] {".gif", ".bmp", ".jpg", ".tif", ".jpeg"};
            foreach (string s in validExt)
            {
                if (Path.GetExtension(fileNamePath).ToUpper() == s.ToUpper())
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Handles the Click event of the button1 control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void button1_Click(object sender, EventArgs e)
        {
            SortMyPictures.Properties.Settings sett = new SortMyPictures.Properties.Settings();
            foreach (ListViewItem lvi in listView1.Items)
            {
                if (lvi.ForeColor != Color.Green)
                {
                    if (lvi.SubItems.Count >= 6)
                    {
                        if (lvi.SubItems[5].Text.Trim().Length > 0)
                        {
                            if (!Directory.Exists(Path.GetDirectoryName(lvi.SubItems[5].Text)))
                            {
                                Directory.CreateDirectory(Path.GetDirectoryName(lvi.SubItems[5].Text));
                            }
                            try
                            {
                                if (!File.Exists(lvi.SubItems[5].Text))
                                {
                                    if (sett.UseCopy)
                                        File.Copy(lvi.SubItems[1].Text, lvi.SubItems[5].Text);
                                    else
                                        File.Move(lvi.SubItems[1].Text, lvi.SubItems[5].Text);
                                }
                                else
                                {
                                    if (sett.UseCopy)
                                        File.Copy(lvi.SubItems[1].Text, UniqueFileName(lvi.SubItems[5].Text));
                                    else
                                        File.Move(lvi.SubItems[1].Text, UniqueFileName(lvi.SubItems[5].Text));
                                }
                                lvi.ForeColor = Color.Green;
                            }
                            catch (IOException)
                            {
                                Console.WriteLine("ERROR --> " + lvi.SubItems[1].Text);
                            }
                        }
                    }
                }
            }
            // Remove any empty folders...
            if (textboxScanLocation.Text == textboxFolder.Text)
            {
                var directory = new DirectoryInfo(textboxScanLocation.Text);

                var caption = "Please confirm";
                var message = "All empty folders will be deleted from" + Environment.NewLine + Environment.NewLine +
                              directory.FullName;

                var result = MessageBox.Show(this, message, caption,
                                             MessageBoxButtons.OKCancel, MessageBoxIcon.Information);

                if (result == DialogResult.OK)
                {
                    //TODO Running = true;
                    cleaner.Clean(directory);
                }
            }
            button1.Enabled = false;
            buttonSort.Enabled = true;
            MessageBox.Show("Move Complete");
        }

        /// <summary>
        /// Uniques the name of the file.
        /// </summary>
        /// <param name="fNamePath">The f name path.</param>
        /// <returns></returns>
        private string UniqueFileName(string fNamePath)
        {
            string origPath = Path.GetDirectoryName(fNamePath);
            string origName = Path.GetFileName(fNamePath);
            string origExt = Path.GetExtension(fNamePath);

            string strippedName = origName.Replace(origExt, "");
            strippedName += DateTime.Now.Ticks.ToString() + origExt;
            return Path.Combine(origPath, strippedName);
        }


        /// <summary>
        /// Handles the Click event of the buttonScanBrowse control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void buttonScanBrowse_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog fbd = new FolderBrowserDialog();
            fbd.Description = "Select Folder";
            fbd.RootFolder = Environment.SpecialFolder.MyComputer;
            fbd.ShowNewFolderButton = false;
            fbd.ShowDialog();

            if (fbd.SelectedPath.Trim().Length > 0)
            {
                textboxScanLocation.Text = fbd.SelectedPath;
            }
        }

        /// <summary>
        /// Handles the Click event of the buttonStop control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void buttonStop_Click(object sender, EventArgs e)
        {
            _StopProcessing = true;
        }

        /// <summary>
        /// Handles the Click event of the button2 control and allows the user to select the My Pictures folder.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void button2_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog fbd = new FolderBrowserDialog();
            fbd.Description = "Select Folder";
            fbd.RootFolder = Environment.SpecialFolder.MyPictures;
            fbd.ShowNewFolderButton = false;
            fbd.ShowDialog();

            if (fbd.SelectedPath.Trim().Length > 0)
            {
                textboxFolder.Text = fbd.SelectedPath;
            }
        }

        /// <summary>
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CleaningDone(object sender, CleaningDoneEventArgs e)
        {
            //Running = false;

            var caption = "Done Cleaning Folders";
            var message = string.Format(CultureInfo.InvariantCulture,
                "{0} folders scanned.{1}{2} folders removed.",
                e.Total,
                Environment.NewLine,
                e.Deleted);

            MessageBox.Show(this, message, caption, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

    }
}